package com.sxau.plantingstructuretuning.service.impl;

import cn.hutool.core.util.NumberUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.sxau.plantingstructuretuning.mapper.CityTableMapper;
import com.sxau.plantingstructuretuning.mapper.CropTableMapper;
import com.sxau.plantingstructuretuning.mapper.KcMapper;
import com.sxau.plantingstructuretuning.mapper.WeatherDataMapper;
import com.sxau.plantingstructuretuning.model.domain.*;
import com.sxau.plantingstructuretuning.model.utils.Result;
import com.sxau.plantingstructuretuning.service.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.Year;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

@Service
@Slf4j
@RequiredArgsConstructor
public class PlantingStructureTuningServiceImpl implements PlantingStructureTuningService {

    private final CityTableMapper cityTableMapper;

    private final CropTableMapper cropTableMapper;

    private final KcMapper kcMapper;

    private final WeatherDataMapper weatherDataMapper;

    // PSO参数
    public int maxIterations = 10; // 最大迭代次数
    public int numParticles = 50; // 粒子数
    public double inertiaWeight = 0.6; // 惯性权重
    public double cognitiveCoefficient = 2.0; // 自我学习因子
    public double socialCoefficient = 2.0; // 社会学习因子

    // 问题参数
    public int numCrops = 38; // 作物种类数
    public double[] cropYield = new double[numCrops]; // 各作物单位面积产量
    public double[] cropPrice= new double[numCrops]; // 各作物单位产量价格
    public double[] cropCost= new double[numCrops]; // 各作物单位面积生产成本
    public double[] cropEcologicalBenefit= new double[numCrops]; // 各作物单位面积生态效益
    public double[] cropWaterRequirement= new double[numCrops]; // 各作物单位面积需水量
    public double[] cropLaborSaving= new double[numCrops]; // 各作物单位面积可解放劳动力数量

    // 区域参数
    public double totalWaterResources; // 可用水资源总量
    public double totalCultivatedArea; // 总耕地面积
    public double demandFood; // 粮食总需求量
    public double requiredTotalOutput; // 农业总产值要求
    public double avgWage; // 外出务工人员平均务工收益

    // 粒子群状态变量
    public double[][] pBest; // 各粒子的历史最优位置
    public double[][] xBest; // 各粒子的当前位置
    public double[][] xVel; // 各粒子的速度
    public double[] gBest; // 全局最优解位置
    public double[][] fitness; // 各粒子的目标函数值
    public double[][] constraints; // 各粒子的约束条件满足情况

    public Random random = new Random();

    @Override
    public Result process(Integer year, String city)  {
        QueryWrapper<CropTable> wrapper = new QueryWrapper<CropTable>()
                .eq("year", year)
                .like("city", city);
        List<CropTable> cropTables = cropTableMapper.selectList(wrapper);
        if (cropTables == null) {
            cropTables = new ArrayList<>(); // 避免空指针
        }
        QueryWrapper<CityTable> cityWrapper = new QueryWrapper<CityTable>()
                .eq("year", year)
                .like("city", city);
        List<CityTable> cityTables = cityTableMapper.selectList(cityWrapper);
        if (cityTables == null) {
            cityTables = new ArrayList<>(); // 避免空指针
        }
        // 初始化问题参数（实际应用中应从数据库或文件中读取）
        List<Double> et0s = getET0s(year, city);
        for (int i = 0; i < et0s.size(); i++) {
            cropWaterRequirement[i] = et0s.get(i); //  各作物单位面积需水量（立方米/公顷）
        }
        for (int i = 0; i < cropTables.size(); i++) {
            CropTable cropTable = cropTables.get(i);
            if (cropTable == null) {
                continue; // 跳过 null 元素
            }
            cropYield[i] = cropTable.getYieldHa();        // 各作物单位面积产量（吨/公顷）
            cropPrice[i] = cropTable.getPriceKg();// 各作物单位产量价格（元/吨）
            cropCost[i] = cropTable.getCostHa(); // 各作物单位面积生产成本（元/公顷）
            cropEcologicalBenefit[i] = cropTable.getEcoValueHa();// 各作物单位面积生态效益（元/公顷）
            cropLaborSaving[i] = cropTable.getLaborNum();// 各作物单位面积可解放劳动力数量（人/公顷）
            totalCultivatedArea = totalCultivatedArea + cropTable.getCostHa();// 总耕地面积（公顷）
        }
        for (Double et0 : getET0s(year, city)) {
            totalWaterResources = totalWaterResources + et0;// 可用水资源总量（立方米）
        }
        for (int i = 0; i < cityTables.size(); i++) {
            CityTable cityTable = cityTables.get(i);
            if (cityTable == null) {
                continue; // 跳过 null 元素
            }
            demandFood = cityTable.getGrainDemand() *
                    cityTable.getBasePopulation() *
                    Math.pow((1+cityTable.getPopGrowthRate()),year - 2016);// 粮食总需求量（吨）
            requiredTotalOutput = cityTable.getPlantingOutput(); // 农业总产值要求（元）
            avgWage = cityTable.getMigrantIncome(); // 外出务工人员平均务工收益（元/人）
        }


        // 初始化PSO
        initializePSO(year,city);

        // 执行PSO算法
        runPSO(year,city);

        return Result.success(gBest);
    }

    private  void initializePSO(Integer year, String city) {
        pBest = new double[numParticles][numCrops];
        xBest = new double[numParticles][numCrops];
        xVel = new double[numParticles][numCrops];
        gBest = new double[numCrops];
        fitness = new double[numParticles][4]; // 四个目标函数值
        constraints = new double[numParticles][4]; // 四个约束条件

        // 随机初始化粒子位置和速度
        for (int i = 0; i < numParticles; i++) {
            for (int j = 0; j < numCrops; j++) {
                xBest[i][j] = random.nextDouble() * totalCultivatedArea; // 种植面积初始化为0到总耕地面积之间
                xVel[i][j] = random.nextDouble() * totalCultivatedArea * 0.1; // 速度初始化
            }
        }

        // 计算初始适应值并初始化pBest和gBest
        for (int i = 0; i < numParticles; i++) {
            evaluateParticle(i,year,city);
            for (int j = 0; j < numCrops; j++) {
                pBest[i][j] = xBest[i][j];
            }
        }

        // 找到初始全局最优解
        double bestFitness = Double.MAX_VALUE;
        for (int i = 0; i < numParticles; i++) {
            double currentFitness = calculateOverallFitness(fitness[i]);
            if (currentFitness < bestFitness) {
                bestFitness = currentFitness;
                for (int j = 0; j < numCrops; j++) {
                    gBest[j] = xBest[i][j];
                }
            }
        }
    }

    private void runPSO(Integer year,String city) {
        for (int iter = 0; iter < maxIterations; iter++) {
            for (int i = 0; i < numParticles; i++) {
                // 更新速度和位置
                for (int j = 0; j < numCrops; j++) {
                    xVel[i][j] = inertiaWeight * xVel[i][j] +
                            cognitiveCoefficient * random.nextDouble() * (pBest[i][j] - xBest[i][j]) +
                            socialCoefficient * random.nextDouble() * (gBest[j] - xBest[i][j]);

                    xBest[i][j] += xVel[i][j];

                    // 限制位置范围
                    if (xBest[i][j] < 0) {
                        xBest[i][j] = 0;
                    }
                    if (xBest[i][j] > totalCultivatedArea) {
                        xBest[i][j] = totalCultivatedArea;
                    }
                }

                // 评估新位置
                evaluateParticle(i,year,city);

                // 更新pBest
                if (calculateOverallFitness(fitness[i]) < calculateOverallFitness(pBestFitness(i))) {
                    for (int j = 0; j < numCrops; j++) {
                        pBest[i][j] = xBest[i][j];
                    }
                }

                // 更新gBest
                if (calculateOverallFitness(fitness[i]) < calculateOverallFitness(gBestFitness(year,city))) {
                    for (int j = 0; j < numCrops; j++) {
                        gBest[j] = xBest[i][j];
                    }
                }
            }

            // 输出当前迭代的全局最优解
            System.out.println("Iteration " + (iter + 1) + ": Best Fitness = " + calculateOverallFitness(gBestFitness(year,city)));
        }

        // 输出最终结果
        System.out.println("Optimization Complete!");
        System.out.println("Best Solution: ");
        for (int j = 0; j < numCrops; j++) {
            System.out.println("Crop " + (j + 1) + " Area: " + gBest[j]);
        }
        System.out.println("Fitness: " + calculateOverallFitness(gBestFitness(year,city)));
    }

    private void evaluateParticle(int particleIndex,Integer year,String city) {
        double[] position = xBest[particleIndex];

        QueryWrapper<CityTable> cityWrapper = new QueryWrapper<CityTable>()
                .eq("year", year)
                .like("city", city);
        List<CityTable> cityTables = cityTableMapper.selectList(cityWrapper);
        double croppingIndex = 0;
        for (CityTable cityTable : cityTables) {
            croppingIndex = cityTable.getCroppingIndex();
        }

        // 计算经济效益
        double economicBenefit = calculateSociety(year,city);

        // 计算社会效益
        double socialBenefit = calculateSociety(year,city);

        // 计算生态效益
        double ecologicalBenefit = calculateEcology(year,city);

        // 计算水分生产效益
        double waterProductionBenefit = calculateWater(year,city);

        // 计算约束条件满足情况
        double totalYield = 0;
        double totalWaterUse = 0;
        double totalOutput = 0;
        double totalArea = 0;
        for (int j = 0; j < numCrops; j++) {
            totalYield += cropYield[j];
            totalWaterUse += cropWaterRequirement[j];
            totalOutput += (cropYield[j] * cropPrice[j] - cropCost[j]) ;
            totalArea += position[j];
        }

        boolean foodSecurity = totalYield >= demandFood;
        boolean waterConstraint = totalWaterUse <= totalWaterResources;
        boolean areaConstraint = totalArea <= totalCultivatedArea * croppingIndex;
        boolean outputConstraint = totalOutput >= requiredTotalOutput;

        // 存储目标函数值和约束条件满足情况
        fitness[particleIndex][0] = economicBenefit;
        fitness[particleIndex][1] = socialBenefit;
        fitness[particleIndex][2] = ecologicalBenefit;
        fitness[particleIndex][3] = waterProductionBenefit;

        constraints[particleIndex][0] = foodSecurity ? 1.0 : 0.0;
        constraints[particleIndex][1] = areaConstraint ? 1.0 : 0.0;
        constraints[particleIndex][2] = waterConstraint ? 1.0 : 0.0;
        constraints[particleIndex][3] = outputConstraint ? 1.0 : 0.0;
    }

    private  double calculateOverallFitness(double[] fitnessValues) {
        // 这里可以根据实际需求定义综合适应度的计算方式
        // 例如，可以简单地将四个目标函数值相加
        return fitnessValues[0] + fitnessValues[1] + fitnessValues[2] + fitnessValues[3];
    }

    private double[] pBestFitness(int particleIndex) {
        double[] particleFitness = new double[4];
        for (int j = 0; j < 4; j++) {
            particleFitness[j] = fitness[particleIndex][j];
        }
        return particleFitness;
    }

    private  double[] gBestFitness(Integer year,String city) {
        double[] fitness = new double[4];
        for (int j = 0; j < 4; j++) {
            fitness[j] = calculateFitness(gBest, j,year,city);
        }
        return fitness;
    }

    private  double calculateFitness(double[] position, int objectiveIndex,Integer year,String city) {
        switch (objectiveIndex) {
            case 0:
                // 计算经济效益
                double economicBenefit = calculateSociety(year,city);
            case 1:
                // 计算社会效益
                double socialBenefit = calculateSociety(year,city);
            case 2:
                // 计算生态效益
                double ecologicalBenefit = calculateEcology(year,city);
            case 3:
                // 计算水分生产效益
                double waterProductionBenefit = calculateWater(year,city);
            default:
                return 0;
        }
    }


/*    @Override
    public void process(Integer year, String city) {
        double economy = calculateEconomy(year, city);
        double society = calculateSociety(year, city);
        double ecology = calculateEcology(year, city);
        double water = calculateWater(year, city);
    }*/

    /**
     * 计算水分生产效益目标函数
     * @param year
     * @param city
     * @return
     */
    private double calculateWater(Integer year, String city) {
        QueryWrapper<CropTable> wrapper = new QueryWrapper<CropTable>()
                .eq("year", year)
                .like("city", city);
        List<CropTable> cropTables = cropTableMapper.selectList(wrapper);
        double areaSum = 0;
        for (CropTable cropTable : cropTables) {
            areaSum += cropTable.getAreaHa();
        }
        List<Double> et0s = getET0s(year, city);
        int sum = 0;
        for (int i = 0;i < cropTables.size();i++) {
            CropTable cropTable = cropTables.get(i);
            double yieldHa = cropTable.getYieldHa();
            double priceKg = cropTable.getPriceKg();
            double costHa = cropTable.getCostHa();
            double areaHa = cropTable.getAreaHa();
            sum += ((yieldHa * priceKg - costHa) / et0s.get(i)) * areaHa /areaSum;
        }
        return NumberUtil.round(sum, 2).doubleValue();
    }

    /**
     * 计算生态效益目标函数
     * @param year
     * @param city
     * @return
     */
    private double calculateEcology(Integer year, String city) {
        QueryWrapper<CropTable> wrapper = new QueryWrapper<CropTable>()
                .eq("year", year)
                .like("city", city);
        List<CropTable> cropTables = cropTableMapper.selectList(wrapper);
        double sum = 0;
        for (CropTable cropTable : cropTables) {
            double ecoValueHa = cropTable.getEcoValueHa();
            double areaHa = cropTable.getAreaHa();
            sum += ecoValueHa * areaHa;
        }
        return NumberUtil.round(sum, 2).doubleValue();
    }

    /**
     * 计算社会效益目标函数
     * @param year
     * @param city
     * @return
     */
    private double calculateSociety(Integer year, String city) {
        QueryWrapper<CropTable> wrapper = new QueryWrapper<CropTable>()
                .eq("year", year)
                .like("city", city);
        List<CropTable> cropTables = cropTableMapper.selectList(wrapper);
        QueryWrapper<CityTable> wrapper1 = new QueryWrapper<CityTable>()
                .eq("year", year)
                .like("city", city);
        CityTable cityTable = cityTableMapper.selectOne(wrapper1);
        double income = cityTable.getMigrantIncome();
        double sum = 0;
        for (CropTable cropTable : cropTables) {
            double yieldHa = cropTable.getYieldHa();
            double priceKg = cropTable.getPriceKg();
            int laborNum = cropTable.getLaborNum();
            double areaHa = cropTable.getAreaHa();
            sum += (yieldHa * priceKg + laborNum * income) * areaHa;
        }
        return NumberUtil.round(sum, 2).doubleValue();
    }

    /**
     * 计算经济产出目标函数
     * @param year
     * @param city
     * @return
     */
    private double calculateEconomy(Integer year, String city) {
        QueryWrapper<CropTable> wrapper = new QueryWrapper<CropTable>().eq("year", year)
                .like("city", city);
        List<CropTable> cropTables = cropTableMapper.selectList(wrapper);
        double sum = 0;
        for (CropTable cropTable : cropTables) {
            double areaHa = cropTable.getAreaHa();
            double priceKg = cropTable.getPriceKg();
            double costHa = cropTable.getCostHa();
            double yieldHa = cropTable.getYieldHa();
            sum += (yieldHa * priceKg - costHa) * areaHa;
        }
        return NumberUtil.round(sum, 2).doubleValue();
    }

    /**
     * 获取第i种作物单位面积的全生育期需水量
     * @param year
     * @param city
     * @return
     */
    public List<Double> getET0s (Integer year, String city) {
        double a = 0.16;
        double b = 0.59;
        int j = 1;
        double month;
        double Tmean, Delta, lat, phi, omega, n, N, RH, U2, d, Ra, Rs, Rns, Rnl, Rn , delta;
        double p, gamma, Rs0, e0max, e0min, es, ea, Tmax, Tmin, G, ET0, kc = 0, sum = 0;
        double precipitation = 0, culArea = 0, precipitationSum = 0;
        ArrayList<Double> list = new ArrayList<>();
        // 构建该年份的起始日期和结束日期
        LocalDate startDate = LocalDate.of(year, 1, 1);
        LocalDate endDate = LocalDate.of(year, 12, 31);
        // 定义日期格式化器，将日期转换为 20160101 格式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
        String startDateTime = startDate.format(formatter);
        String endDateTime = endDate.format(formatter);
        QueryWrapper<WeatherData> weatherWrapper = new QueryWrapper<WeatherData>()
                .between("date_time", startDateTime, endDateTime)
                .like("reference_location", city);
        List<WeatherData> weatherDataList = weatherDataMapper.selectList(weatherWrapper);
        List<Kc> kcs = kcMapper.selectList(null);
        for (Kc kc0 : kcs) {
            for (WeatherData weatherData : weatherDataList) {
                Tmean = weatherData.getAvgTemperature();//平均气温2m
                lat = weatherData.getLatitude();//纬度
                n = weatherData.getSunshineHours();
                RH = weatherData.getRelativeHumidity();
                U2 = weatherData.getWindSpeed();
                p = weatherData.getSurfacePressure();
                Tmax = weatherData.getMaxTemperature();
                Tmin = weatherData.getMinTemperature();

                Delta = (2504 * (Math.exp(17.27 * Tmean / (Tmean + 237.3)))) / ((Tmean + 237.3) * (Tmean + 237.3));
                delta = 0.409 * Math.sin(0.0172 * j - 1.39);//jisuan日倾角
                phi = lat * Math.PI / 180.0;
                omega = Math.acos(-Math.tan(phi) * Math.tan(delta));
                N = (24 / Math.PI) * omega;
                d = 1 - 0.033 * Math.cos(0.0172 * j);
                Ra = 37.6 * d * (omega * Math.sin(phi) * Math.sin(delta) * Math.cos(phi) + Math.cos(delta) * Math.sin(omega));
                Rs = (a + b * n / N) * Ra;
                Rns = 0.77 * Rs;
                gamma = 0.0665 * 0.001 * p / 10;
                Rs0 = (a + b) * Ra;
                e0max = 0.611 * Math.exp((17.27 * Tmax) / (Tmax + 237.3));
                e0min = 0.611 * Math.exp((17.27 * Tmin) / (Tmin + 237.3));
                es = (e0max + e0min) / 2;
                ea = (RH / 100) * es;
                Rnl = 4.903 * Math.pow(10,-9) * (((Math.pow((Tmax + 273.15), 4)) + (Math.pow((Tmin + 273.15), 4))) / 2) * (0.34 - 0.14 * Math.sqrt(ea)) * (1.35 * (Rs / Rs0) - 0.35);
                Rn = Rns - Rnl;

                if (j == 1) {
                    G = 0.1 * (Tmean - weatherDataList.get(weatherDataList.size() - 1).getAvgTemperature());
                } else {
                    G = 0.1 * (Tmean - weatherData.getAvgTemperature());
                }

                ET0 = ((0.408 * Delta * (Rn - G)) + (gamma * 900  * U2 * (es - ea)/ (Tmean + 273))) / (Delta + gamma * (1 + 0.34 * U2 * 0.747951));
                month = Year.of(year).atDay(j).getMonthValue();
                for (int i = 0;i < kcs.size(); i++) {
                    if (month == 1) {
                        kc = kc0.getKc1();
                    } else if (month == 2) {
                        kc = kc0.getKc2();
                    } else if (month == 3) {
                        kc = kc0.getKc3();
                    } else if (month == 4) {
                        kc = kc0.getKc4();
                    } else if (month == 5) {
                        kc = kc0.getKc5();
                    } else if (month == 6) {
                        kc = kc0.getKc6();
                    } else if (month == 7) {
                        kc = kc0.getKc7();
                    } else if (month == 8) {
                        kc = kc0.getKc8();
                    } else if (month == 9) {
                        kc = kc0.getKc9();
                    } else if (month == 10) {
                        kc = kc0.getKc10();
                    } else if (month == 11) {
                        kc = kc0.getKc11();
                    } else if (month == 12) {
                        kc = kc0.getKc12();
                    }
                    if (kc != 0) {
                        sum += kc * ET0;
                    }
                }
                //System.out.println("现在是" + j + ":   " + sum + ", 降水量为:  " + precipitationSum);
                j++;
            }
            list.add(sum);
            sum = 0;
            j = 1;
        }
        return list;
    }

}
